﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using GameUtils.Input;
using Microsoft.Xna.Framework.Graphics;

namespace GameControls
{
    public sealed class Slider : ControlBase
    {
        #region Constants
        private const float MIN_KNOB_FRACTION = 0.01f;
        private const float MIN_SLIDER_RANGE = 0.1f;
        private const float MAX_SLIDER_RANGE = 10e6f;
        #endregion

        #region Properties
        #region KnobPosition
        private float mKnobPosition;

        /// <summary>
        /// KnobPosition property.
        /// </summary>
        public float KnobPosition
        {
            get { return mKnobPosition; }
            private set
            {
                float newKnobPosition = MathHelper.Clamp(value, SliderStartValue, SliderStartValue + SliderRange - KnobSize);
                if (mKnobPosition != newKnobPosition)
                {
                    mKnobPosition = newKnobPosition;
                    RaiseKnobPositionChangedEvent();
                }
            }
        }
        #endregion

        #region KnobSize
        private float mKnobSize;

        /// <summary>
        /// KnobSize property.
        /// </summary>
        public float KnobSize
        {
            get { return mKnobSize; }
            set
            {
                float newKnobSize = MathHelper.Clamp(value, SliderRange * MIN_KNOB_FRACTION, SliderRange);
                if (mKnobSize != value)
                {
                    mKnobSize = newKnobSize;
                    KnobPosition = mKnobPosition;
                }
            }
        }
        #endregion

        #region SliderStartValue
        private float mSliderStartValue;

        /// <summary>
        /// SliderStartValue property.
        /// </summary>
        public float SliderStartValue
        {
            get { return mSliderStartValue; }
            set
            {
                if (mSliderStartValue != value)
                {
                    mSliderStartValue = value;
                    KnobPosition = mKnobPosition;
                }
            }
        }
        #endregion

        #region SliderRange
        private float mSliderRange;

        /// <summary>
        /// SliderRange property.
        /// </summary>
        public float SliderRange
        {
            get { return mSliderRange; }
            set
            {
                float newSliderRange = MathHelper.Clamp(value, MIN_SLIDER_RANGE, MAX_SLIDER_RANGE);
                if (mSliderRange != value)
                {
                    mSliderRange = newSliderRange;
                    KnobSize = mKnobSize;
                }
            }
        }
        #endregion

        #region Texture
        /// <summary>
        /// Texture property.
        /// </summary>
        public Texture2D Texture { get; private set; }
        #endregion

        private bool mIsButtonDown;
        #endregion

        #region Events
        #region KnobPositionChanged
        public event EventHandler KnobPositionChanged;

        private void RaiseKnobPositionChangedEvent()
        {
            var handler = KnobPositionChanged;
            if (handler != null)
            {
                handler(this, EventArgs.Empty);
            }
        }
        #endregion
        #endregion

        #region Constructors
        public Slider(Rectangle pRect, Texture2D pTexture)
            : base(pRect)
        {
            Texture = pTexture;

            mIsButtonDown = false;

            mKnobPosition = 0;
            mKnobSize = 1;
            mSliderStartValue = 0;
            mSliderRange = 1;
        }
        #endregion

        #region Overriden Methods
        protected override void UpdateImpl(GameTime pGameTime)
        {
        }

        #region Mouse
        protected override void OnLeftButtonDownImpl(MouseEventArgs pArgs)
        {
            mIsButtonDown = IsMouseOver(pArgs.X, pArgs.Y);
            if (mIsButtonDown)
            {
                MouseCapture.Capture(this);
                KnobPosition = GetKnobPosition(pArgs.X, pArgs.Y);
            }
        }

        protected override void OnLeftButtonUpImpl(MouseEventArgs pArgs)
        {
            if (mIsButtonDown)
            {
                KnobPosition = GetKnobPosition(pArgs.X, pArgs.Y);
            }
            mIsButtonDown = false;
        }

        protected override void OnMouseMoveImpl(MouseEventArgs pArgs)
        {
            if (mIsButtonDown)
            {
                KnobPosition = GetKnobPosition(pArgs.X, pArgs.Y);
            }
        }
        #endregion
        #endregion

        #region Methods
        public Rectangle GetKnobRect()
        {
            Rectangle transformedRect = TransformedRect;
            int y = transformedRect.Y + (int)(transformedRect.Height * (KnobPosition - SliderStartValue) / SliderRange);
            int height = (int)(transformedRect.Height * KnobSize / SliderRange) + 1; // + 1 because it looks better, knob rectangle reaches bottom of slider
            return new Rectangle(
                transformedRect.X,
                y,
                transformedRect.Width,
                height);
        }

        private float GetKnobPosition(int pMouseX, int pMouseY)
        {
            Rectangle transformedRect = TransformedRect;
            float knobPosition = ((float)(pMouseY - transformedRect.Y) / (float)transformedRect.Height) * SliderRange - KnobSize / 2.0f + SliderStartValue;
            return knobPosition;
        }
        #endregion
    }
}